代码审查技能概述#
代码审查技能是 Claude Code Skills 中用于自动化代码审查的重要工具。它可以帮助开发者快速识别代码中的问题,提高代码质量和可维护性。
审查类型#
1. 静态代码分析#
1.1 代码质量检查
python# src/skills/code_reviewer.py from typing import Dict, Any, List from claude_code_sdk import Skill, SkillContext, SkillResult import re class CodeReviewerSkill(Skill): """代码审查技能""" def __init__(self): super().__init__( name="code-reviewer", version="1.0.0", description="Automated code review skill" ) # 定义审查规则 self.rules = { "naming": self.check_naming_conventions, "complexity": self.check_complexity, "security": self.check_security, "performance": self.check_performance, "documentation": self.check_documentation } def get_parameters_schema(self) -> Dict[str, Any]: return { "type": "object", "properties": { "file_path": { "type": "string", "description": "Path to the file to review" }, "code": { "type": "string", "description": "Code to review (alternative to file_path)" }, "language": { "type": "string", "enum": ["python", "javascript", "java", "go"], "description": "Programming language" }, "rules": { "type": "array", "description": "Rules to apply", "items": { "type": "string", "enum": ["naming", "complexity", "security", "performance", "documentation"] } }, "severity": { "type": "string", "enum": ["all", "error", "warning", "info"], "description": "Minimum severity level" } }, "required": ["language"] } def execute(self, parameters: Dict[str, Any], context: SkillContext) -> SkillResult: try: language = parameters["language"] rules_to_apply = parameters.get("rules", list(self.rules.keys())) severity = parameters.get("severity", "all") # 获取代码 if "file_path" in parameters: code = context.read_file(parameters["file_path"]) file_path = parameters["file_path"] elif "code" in parameters: code = parameters["code"] file_path = "<inline>" else: return SkillResult( success=False, error="Either file_path or code must be provided" ) # 执行审查 issues = [] for rule_name in rules_to_apply: if rule_name in self.rules: rule_func = self.rules[rule_name] rule_issues = rule_func(code, language) issues.extend(rule_issues) # 过滤严重性 if severity != "all": severity_levels = {"error": 3, "warning": 2, "info": 1} min_level = severity_levels.get(severity, 0) issues = [ issue for issue in issues if severity_levels.get(issue["severity"], 0) >= min_level ] # 生成报告 report = self.generate_report(code, issues, file_path) return SkillResult( success=True, data={ "file_path": file_path, "language": language, "rules_applied": rules_to_apply, "issues": issues, "issue_count": len(issues), "report": report } ) except Exception as e: return SkillResult( success=False, error=str(e) ) def check_naming_conventions(self, code: str, language: str) -> List[Dict]: """检查命名规范""" issues = [] if language == "python": # 检查函数名(应该是 snake_case) func_pattern = r'def\s+([A-Z][a-zA-Z0-9_]*)\s*\(' for match in re.finditer(func_pattern, code): line_num = code[:match.start()].count('\n') + 1 issues.append({ "type": "naming", "severity": "warning", "line": line_num, "message": f"Function name '{match.group(1)}' should use snake_case", "suggestion": match.group(1).lower() }) # 检查类名(应该是 CamelCase) class_pattern = r'class\s+([a-z][a-zA-Z0-9_]*)\s*[:\(]' for match in re.finditer(class_pattern, code): line_num = code[:match.start()].count('\n') + 1 issues.append({ "type": "naming", "severity": "warning", "line": line_num, "message": f"Class name '{match.group(1)}' should use CamelCase", "suggestion": match.group(1).title() }) elif language == "javascript": # 检查常量(应该是 UPPER_CASE) const_pattern = r'const\s+([a-z][a-zA-Z0-9_]*)\s*=' for match in re.finditer(const_pattern, code): line_num = code[:match.start()].count('\n') + 1 issues.append({ "type": "naming", "severity": "info", "line": line_num, "message": f"Constant '{match.group(1)}' should use UPPER_CASE", "suggestion": match.group(1).upper() }) return issues def check_complexity(self, code: str, language: str) -> List[Dict]: """检查复杂度""" issues = [] lines = code.split('\n') for i, line in enumerate(lines, 1): # 计算缩进级别 indent = len(line) - len(line.lstrip()) # 检查过深的嵌套 if indent > 24: issues.append({ "type": "complexity", "severity": "warning", "line": i, "message": f"Deep nesting detected (indent level: {indent // 4})", "suggestion": "Consider refactoring to reduce nesting" }) # 检查长行 if len(line) > 120: issues.append({ "type": "complexity", "severity": "info", "line": i, "message": f"Line too long ({len(line)} characters)", "suggestion": "Break the line into multiple lines" }) return issues ```python def check_security(self, code: str, language: str) -> List[Dict]: """检查安全问题""" issues = [] if language == "python": # 检查 eval 使用 eval_pattern = r'\beval\s*\(' for match in re.finditer(eval_pattern, code): line_num = code[:match.start()].count('\n') + 1 issues.append({ "type": "security", "severity": "error", "line": line_num, "message": "Use of eval() is dangerous", "suggestion": "Use ast.literal_eval() or alternative safe methods" }) # 检查 exec 使用 exec_pattern = r'\bexec\s*\(' for match in re.finditer(exec_pattern, code): line_num = code[:match.start()].count('\n') + 1 issues.append({ "type": "security", "severity": "error", "line": line_num, "message": "Use of exec() is dangerous", "suggestion": "Avoid using exec() for security reasons" }) # 检查硬编码密码 password_pattern = r'(password|passwd|pwd)\s*=\s*["\'][^"\']+["\']' for match in re.finditer(password_pattern, code, re.IGNORECASE): line_num = code[:match.start()].count('\n') + 1 issues.append({ "type": "security", "severity": "warning", "line": line_num, "message": "Hardcoded password detected", "suggestion": "Use environment variables or configuration files" }) elif language == "javascript": # 检查 innerHTML 使用 innerhtml_pattern = r'\.innerHTML\s*=' for match in re.finditer(innerhtml_pattern, code): line_num = code[:match.start()].count('\n') + 1 issues.append({ "type": "security", "severity": "warning", "line": line_num, "message": "Use of innerHTML can lead to XSS vulnerabilities", "suggestion": "Use textContent or sanitize input" }) return issues def check_performance(self, code: str, language: str) -> List[Dict]: """检查性能问题""" issues = [] if language == "python": # 检查循环中的字符串拼接 lines = code.split('\n') in_loop = False loop_indent = 0 for i, line in enumerate(lines, 1): # 检测循环 if re.match(r'\s*(for|while)\s+', line): in_loop = True loop_indent = len(line) - len(line.lstrip()) elif in_loop and len(line) - len(line.lstrip()) <= loop_indent: in_loop = False # 检查字符串拼接 if in_loop and '+=' in line and '"' in line and "'" in line: issues.append({ "type": "performance", "severity": "warning", "line": i, "message": "String concatenation in loop may be inefficient", "suggestion": "Use list and join() for better performance" }) return issues def check_documentation(self, code: str, language: str) -> List[Dict]: """检查文档""" issues = [] if language == "python": # 检查函数是否有文档字符串 func_pattern = r'def\s+(\w+)\s*\([^)]*\):' for match in re.finditer(func_pattern, code): func_name = match.group(1) func_start = match.end()
检查下一行是否有文档字符串
remaining_code = code[func_start:func_start + 100] if not re.search(r'\s*"""', remaining_code): line_num = code[:match.start()].count('\n') + 1 issues.append({ "type": "documentation", "severity": "info", "line": line_num, "message": f"Function '{func_name}' lacks docstring", "suggestion": "Add a docstring to describe the function" })
return issues
def generate_report(self, code: str, issues: List[Dict], file_path: str) -> str: """生成审查报告""" report = f"# Code Review Report for {file_path}\n\n"
统计
error_count = sum(1 for i in issues if i["severity"] == "error") warning_count = sum(1 for i in issues if i["severity"] == "warning") info_count = sum(1 for i in issues if i["severity"] == "info")
report += f"## Summary\n\n" report += f"- Total Issues: {len(issues)}\n" report += f"- Errors: {error_count}\n" report += f"- Warnings: {warning_count}\n" report += f"- Info: {info_count}\n\n"
按类型分组
issues_by_type = {} for issue in issues: issue_type = issue["type"] if issue_type not in issues_by_type: issues_by_type[issue_type] = [] issues_by_type[issue_type].append(issue)
详细问题
for issue_type, type_issues in issues_by_type.items(): report += f"## {issue_type.title()} Issues ({len(type_issues)})\n\n"
for issue in type_issues: severity_icon = { "error": "❌", "warning": "⚠️", "info": "ℹ️" }.get(issue["severity"], "•")
report += f"{severity_icon} Line {issue['line']}: {issue['message']}\n" if "suggestion" in issue: report += f" 💡 Suggestion: {issue['suggestion']}\n" report += "\n"
return report
2. 代码风格检查#
2.1 PEP 8 检查
bashpython # src/skills/pep8_checker.py from typing import Dict, Any, List from claude_code_sdk import Skill, SkillContext, SkillResult import re
class PEP8CheckerSkill(Skill): """PEP 8 代码风格检查技能"""
bashdef __init__(self): super().__init__( name="pep8-checker", version="1.0.0", description="PEP 8 style checker" ) def get_parameters_schema(self) -> Dict[str, Any]: return { "type": "object", "properties": { "file_path": { "type": "string", "description": "Path to the file to check" }, "code": { "type": "string", "description": "Code to check" }, "max_line_length": { "type": "integer", "description": "Maximum line length", "default": 79 } } } def execute(self, parameters: Dict[str, Any], context: SkillContext) -> SkillResult: try: max_line_length = parameters.get("max_line_length", 79) # 获取代码 if "file_path" in parameters: code = context.read_file(parameters["file_path"]) file_path = parameters["file_path"] elif "code" in parameters: code = parameters["code"] file_path = "<inline>" else: return SkillResult( success=False, error="Either file_path or code must be provided" ) # 执行检查 violations = self.check_pep8(code, max_line_length) return SkillResult( success=True, data={ "file_path": file_path, "max_line_length": max_line_length, "violations": violations, "violation_count": len(violations) } ) except Exception as e: return SkillResult( success=False, error=str(e) ) def check_pep8(self, code: str, max_line_length: int) -> List[Dict]: """检查 PEP 8 规范""" violations = [] lines = code.split('\n') for i, line in enumerate(lines, 1): # 检查行长度 if len(line) > max_line_length: violations.append({ "line": i, "code": "E501", "message": f"Line too long ({len(line)} > {max_line_length} characters)", "severity": "warning" }) # 检查尾随空格 if line.rstrip() != line.rstrip('\n').rstrip('\r'): violations.append({ "line": i, "code": "W291", "message": "Trailing whitespace", "severity": "warning" }) # 检查空行 if line.strip() == "" and i < len(lines): # 检查连续空行 if i > 1 and lines[i-2].strip() == "" and lines[i-1].strip() == "": violations.append({ "line": i, "code": "E303", "message": "Too many blank lines", "severity": "info" }) # 检查导入顺序 if line.strip().startswith("import ") or line.strip().startswith("from "): if i > 1 and lines[i-2].strip() and not ( lines[i-2].strip().startswith("import ") or lines[i-2].strip().startswith("from ") ): violations.append({ "line": i, "code": "E402", "message": "Module level import not at top of file", "severity": "error" }) return violations
3. 代码重复检测#
3.1 重复代码检查
python# src/skills/duplicate_detector.py from typing import Dict, Any, List, Tuple from claude_code_sdk import Skill, SkillContext, SkillResult import difflib class DuplicateDetectorSkill(Skill): """重复代码检测技能""" def __init__(self): super().__init__( name="duplicate-detector", version="1.0.0", description="Detect duplicate code blocks" ) def get_parameters_schema(self) -> Dict[str, Any]: return { "type": "object", "properties": { "file_path": { "type": "string", "description": "Path to the file to check" }, "code": { "type": "string", "description": "Code to check" }, "min_lines": { "type": "integer", "description": "Minimum number of lines to consider as duplicate", "default": 5 }, "similarity_threshold": { "type": "number", "description": "Similarity threshold (0.0 to 1.0)", "default": 0.8 } } } def execute(self, parameters: Dict[str, Any], context: SkillContext) -> SkillResult: try: min_lines = parameters.get("min_lines", 5) similarity_threshold = parameters.get("similarity_threshold", 0.8) # 获取代码 if "file_path" in parameters: code = context.read_file(parameters["file_path"]) file_path = parameters["file_path"] elif "code" in parameters: code = parameters["code"] file_path = "<inline>" else: return SkillResult( success=False, error="Either file_path or code must be provided" ) # 检测重复 duplicates = self.detect_duplicates(code, min_lines, similarity_threshold) return SkillResult( success=True, data={ "file_path": file_path, "min_lines": min_lines, "similarity_threshold": similarity_threshold, "duplicates": duplicates, "duplicate_count": len(duplicates) } ) except Exception as e: return SkillResult( success=False, error=str(e) ) def detect_duplicates(self, code: str, min_lines: int, similarity_threshold: float) -> List[Dict]: """检测重复代码块""" lines = code.split('\n') duplicates = [] # 提取代码块 blocks = self.extract_blocks(lines, min_lines) # 比较所有块对 for i, block1 in enumerate(blocks): for j, block2 in enumerate(blocks): if i >= j: continue similarity = self.calculate_similarity(block1["code"], block2["code"]) if similarity >= similarity_threshold: duplicates.append({ "block1": { "start_line": block1["start_line"], "end_line": block1["end_line"], "code": block1["code"] }, "block2": { "start_line": block2["start_line"], "end_line": block2["end_line"], "code": block2["code"] }, "similarity": similarity, "suggestion": "Consider extracting common code into a function" }) return duplicates def extract_blocks(self, lines: List[str], min_lines: int) -> List[Dict]: """提取代码块""" blocks = [] for i in range(len(lines) - min_lines + 1): block_code = '\n'.join(lines[i:i+min_lines]) blocks.append({ "start_line": i + 1, "end_line": i + min_lines, "code": block_code }) return blocks def calculate_similarity(self, code1: str, code2: str) -> float: """计算代码相似度""" # 使用序列匹配器 matcher = difflib.SequenceMatcher(None, code1, code2) return matcher.ratio()
使用示例#
1. 完整代码审查#
bashpython # examples/code_review.py from skills.code_reviewer import CodeReviewerSkill from claude_code_sdk import SkillContext skill = CodeReviewerSkill() context = SkillContext() result = skill.execute( { "file_path": "src/main.py", "language": "python", "rules": ["naming", "complexity", "security", "performance", "documentation"], "severity": "all" }, context ) print(f"Found {result.data['issue_count']} issues") print(result.data["report"])
2. PEP 8 检查#
python# examples/pep8_check.py from skills.pep8_checker import PEP8CheckerSkill from claude_code_sdk import SkillContext skill = PEP8CheckerSkill() context = SkillContext() result = skill.execute( { "file_path": "src/main.py", "max_line_length": 79 }, context ) print(f"Found {result.data['violation_count']} PEP 8 violations") for violation in result.data["violations"]: print(f"Line {violation['line']}: {violation['message']}")
3. 重复代码检测#
bashpython # examples/duplicate_detection.py from skills.duplicate_detector import DuplicateDetectorSkill from claude_code_sdk import SkillContext skill = DuplicateDetectorSkill() context = SkillContext() result = skill.execute( { "file_path": "src/main.py", "min_lines": 5, "similarity_threshold": 0.8 }, context ) print(f"Found {result.data['duplicate_count']} duplicate blocks") for duplicate in result.data["duplicates"]: print(f"Lines {duplicate['block1']['start_line']}-{duplicate['block1']['end_line']} " f"similar to lines {duplicate['block2']['start_line']}-{duplicate['block2']['end_line']} " f"(similarity: {duplicate['similarity']:.2f})") ## 最佳实践 ### 1. 审查规则配置 #### 1. 规则优先级 - 安全规则:最高优先级 - 性能规则:高优先级 - 代码质量:中优先级 - 代码风格:低优先级 ### 2. 规则定制 - 根据项目需求定制规则 - 考虑团队编码规范 - 逐步引入新规则 ### 3. 规则例外 - 提供规则例外机制 - 记录例外原因 - 定期审查例外
2. 审查流程#
bashmarkdown #### 1. 自动审查 - 在提交前自动运行 - 集成到 CI/CD 流程 - 阻止不符合规范的代码 ### 2. 人工审查 - 审查自动审查结果 - 关注复杂逻辑 - 提供建设性反馈 ### 3. 持续改进 - 收集审查反馈 - 优化审查规则 - 提高审查效率
总结#
代码审查技能可以帮助团队自动化代码审查流程,提高代码质量和一致性。通过合理配置审查规则和流程,可以显著减少代码中的问题,提高开发效率。
在下一节中,我们将探讨文档生成技能。